package com.ksana.context.event;

import com.ksana.bean.factory.BeanClassLoaderAware;
import com.ksana.bean.factory.BeanFactory;
import com.ksana.bean.factory.BeanFactoryAware;
import com.ksana.context.ApplicationEvent;
import com.ksana.context.ApplicationListener;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware {
    private ClassLoader classLoader;
    private BeanFactory beanFactory;
    private Set<ApplicationListener<?>> applicationListeners = new HashSet<>();
    private Set<String> applicationListenerBeans = new HashSet<>();

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        this.classLoader = classLoader;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    @Override
    public void addApplicationListener(ApplicationListener<?> listener) {
        this.applicationListeners.add(listener);
    }

    @Override
    public void addApplicationListenerBean(String listenerBeanName) {
        this.applicationListenerBeans.add(listenerBeanName);
    }

    @Override
    public void removeApplicationListener(ApplicationListener<?> listener) {
        this.applicationListeners.remove(listener);
    }

    @Override
    public void removeApplicationListenerBean(String listenerBeanName) {
        this.applicationListenerBeans.remove(listenerBeanName);
    }

    public Collection<ApplicationListener<?>> getApplicationListeners(ApplicationEvent event) {
        List<ApplicationListener<?>> applicationListeners = new ArrayList<>();

        for (ApplicationListener applicationListener : this.applicationListeners) {
            if (supportsEvent(applicationListener, event)) {
                applicationListeners.add(applicationListener);
            }
        }
        return applicationListeners;
    }

    private boolean supportsEvent(ApplicationListener listener, ApplicationEvent event) {
        Class<? extends ApplicationListener> listenerClass = listener.getClass();
        Class targetClass = event.getClass();
        ParameterizedType genericType = (ParameterizedType) listenerClass.getSuperclass().getGenericInterfaces()[0];
        Class eventClass = (Class) genericType.getActualTypeArguments()[0];

        return targetClass.isAssignableFrom(eventClass);
    }
}
